package com.pine.app.module.security.oauth.config;


import com.google.common.collect.Lists;
import com.pine.app.module.security.core.common.AuthConstant;
import com.pine.app.module.security.core.common.SpringContextUtils;
import com.pine.app.module.security.core.matcher.AntPathRequestMatcher;
import com.pine.app.module.security.core.matcher.OrRequestMatcher;
import com.pine.app.module.security.core.matcher.RequestMatcher;
import com.pine.app.module.security.oauth.config.properties.LoginProperties;
import com.pine.app.module.security.oauth.filter.ExceptionTranslationFilter;
import com.pine.app.module.security.oauth.filter.OAuth2AuthenticationProcessingFilter;
import com.pine.app.module.security.oauth.filter.SecurityContextFilter;
import com.pine.app.module.security.oauth.handler.AuthticationHandler;
import com.pine.app.module.security.oauth.plugin.impl.loginAttemp.InMemoryLoginAttemptServiceImpl;
import com.pine.app.module.security.oauth.plugin.impl.loginAttemp.LoginAttempPlugin;
import com.pine.app.module.security.oauth.plugin.impl.loginAttemp.LoginAttempService;
import com.pine.app.module.security.oauth.plugin.intercept.PluginAspect;
import com.pine.app.module.security.oauth.plugin.intercept.PluginInterceptor;
import com.pine.app.module.security.oauth.provider.AuthenticationManager;
import com.pine.app.module.security.oauth.user.password.BCryptPasswordEncoder;
import com.pine.app.module.security.oauth.user.password.DefaultPasswordVerifiers;
import com.pine.app.module.security.oauth.user.password.PasswordEncoder;
import com.pine.app.module.security.oauth.user.password.PasswordVerifiers;
import com.pine.app.module.security.oauth.user.service.IntergrationUserDetailService;
import com.pine.app.module.security.oauth.user.service.LoginService;
import com.pine.app.module.security.oauth.user.service.LoginServiceImpl;
import com.pine.app.module.security.oauth.user.service.UserDetailService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author xiaoyuan
 * @create 2020/3/14 0:24
 **/
@Getter
@Slf4j
public class WebSecurityConfiguration implements ApplicationContextAware, InitializingBean {


    private ApplicationContext applicationContext;


    @Autowired
    private LoginProperties loginProperty;

    public WebSecurityConfiguration() {

    }

    //异常处理拦截器
    @Bean
    public ExceptionTranslationFilter exceptionTranslationFilter() {
        return new ExceptionTranslationFilter();
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    //登陆拦截器
    @Bean
    @ConditionalOnMissingBean(OAuth2AuthenticationProcessingFilter.class)
    public OAuth2AuthenticationProcessingFilter loginUrlAuthenticationFilter() {
        List<String> paths = loginProperty.getIgnoreLoginPath() == null ? Lists.newArrayList() : Arrays.asList(loginProperty.getIgnoreLoginPath().split(","));
        paths.add("/static/**");
        paths.add("/oauth/**");
        paths.add("/login/**");
        paths.add("/logout/**");
        List<RequestMatcher> list = new ArrayList<>();
        paths.forEach(s -> {
            list.add(new AntPathRequestMatcher(s));
        });
        return new OAuth2AuthenticationProcessingFilter(new OrRequestMatcher(list));
    }


    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Bean
    @ConditionalOnMissingBean(LoginAttempService.class)
    @ConditionalOnProperty(prefix = LoginProperties.LOGIN_PROPERTIES+".attemp",name = "enable",havingValue = "true",matchIfMissing = true)
    public LoginAttempService loginAttempService() {
        return new InMemoryLoginAttemptServiceImpl(loginProperty);
    }

    @Bean
    public AuthticationHandler authticationHandler() {
        return new AuthticationHandler();
    }

    @Bean
    @ConditionalOnBean(LoginAttempService.class)
    @ConditionalOnProperty(prefix = LoginProperties.LOGIN_PROPERTIES+".attemp",name = "enable",havingValue = "true",matchIfMissing = true)
    public LoginAttempPlugin loginAttempPlugin() {
        return new LoginAttempPlugin(loginProperty);
    }

    //上下文处理的拦截器
    @Bean
    @ConditionalOnBean(AuthenticationManager.class)
    public SecurityContextFilter securityContextFilter() {
        return new SecurityContextFilter();
    }

    @Bean
    @ConditionalOnMissingBean(UserDetailService.class)
    public UserDetailService userDetailService() {
        return new IntergrationUserDetailService();
    }


    @Bean
    @ConditionalOnMissingBean(PasswordEncoder.class)
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @ConditionalOnBean(PasswordEncoder.class)
    @ConditionalOnMissingBean(PasswordVerifiers.class)
    public PasswordVerifiers passwordVerifiers() {
        return new DefaultPasswordVerifiers();
    }

    @Bean
    @ConditionalOnMissingBean(LoginService.class)
    @ConditionalOnBean({UserDetailService.class, PasswordVerifiers.class})
    public LoginService loginService() {
        return new LoginServiceImpl();
    }


    @Bean
    public PluginAspect pluginAspect() {
        return new PluginAspect();
    }

    @Bean
    public PluginInterceptor pluginInterceptor() {
        return new PluginInterceptor();
    }

    @Bean
    public SpringContextUtils springContextUtils() {
        return new SpringContextUtils();
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        if(StringUtils.isNotEmpty(loginProperty.getLoginPage())){
            AuthConstant.DEFAULT_LOGIN_PAGE = loginProperty.getLoginPage();
        }
        if(StringUtils.isNotEmpty(loginProperty.getLoginErrorPage())){
            AuthConstant.DEFAULT_LOGIN_ERROR_PAGE = loginProperty.getLoginErrorPage();
        }

    }
}
